/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.internal.util.xml;

import java.security.AccessController;
import java.security.PrivilegedAction;

import org.dom4j.DocumentFactory;
import org.dom4j.io.SAXReader;
import org.xml.sax.EntityResolver;

/**
 * Small helper class that lazily loads DOM and SAX reader and keep them for fast use afterwards.
 *
 * This was part of Hibernate ORM core, but moved into the testsuite helpers to not expose
 * access to the dom4j types. It's also used by Hibernate Envers, so we will need two copies
 * until Envers is able to remove its reliance on dom4j.
 * The rest of Hibernate uses StAX now for XML processing.  See {@link org.hibernate.boot.jaxb.internal.stax}
 */
public final class XMLHelper {
	private final DocumentFactory documentFactory;

	public XMLHelper() {
		PrivilegedAction<DocumentFactory> action = new PrivilegedAction<DocumentFactory>() {
			public DocumentFactory run() {
				final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader();
				try {
					// We need to make sure we get DocumentFactory
					// loaded from the same ClassLoader that loads
					// Hibernate classes, to make sure we get the
					// proper version of DocumentFactory, This class
					// is "internal", and should only be used for XML
					// files generated by Envers.

					// Using the (Hibernate) ClassLoader that loads
					// this Class will avoid collisions in the case
					// that DocumentFactory can be loaded from,
					// for example, the application ClassLoader.
					Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
					return DocumentFactory.getInstance();
				}
				finally {
					Thread.currentThread().setContextClassLoader( originalTccl );
				}
			}
		};

		this.documentFactory = System.getSecurityManager() != null
				? AccessController.doPrivileged( action )
				: action.run();
	}

	public DocumentFactory getDocumentFactory() {
		return documentFactory;
	}

	public SAXReader createSAXReader(ErrorLogger errorLogger, EntityResolver entityResolver) {
		SAXReader saxReader = new SAXReader();
		try {
			saxReader.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false );
			saxReader.setFeature( "http://xml.org/sax/features/external-general-entities", false );
			saxReader.setFeature( "http://xml.org/sax/features/external-parameter-entities", false );
		}
		catch (Exception e) {
			throw new RuntimeException( e );
		}
		saxReader.setMergeAdjacentText( true );
		saxReader.setValidation( true );
		saxReader.setErrorHandler( errorLogger );
		saxReader.setEntityResolver( entityResolver );

		return saxReader;
	}
}
